package com.jaws.core.mvc.view;

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.IOUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.ModelAndView;

import com.jaws.core.common.excel.JawsPoiExporter;
import com.jaws.core.common.excel.cache.ExcelTemplateCache;
import com.jaws.core.common.excel.domain.JawsExcelTemplate;
import com.jaws.core.common.exception.JawsCommonExceptionCode;
import com.jaws.core.common.log.Log;

/**
 * excel视图类
 * @author maojian
 * @date 2018-07-05 10:59:56
 * @since v1.0.0
 */
@Slf4j
public class JawsExcelView extends JawsAbstractView {

	public static String VIEW = "jawsExcelView";

	public static final String TEMPLATE = "template";

	private static final String DATE_FORMAT = "yyyy-MM-dd";

	@Value("${spring.profiles.active:null}")
	private String active;

	@Override
	public String getView() {
		return VIEW;
	}

	/**
	 * Default Constructor.
	 * Sets the content type of the view to "application/vnd.ms-excel".
	 */
	public JawsExcelView() {
		// setContentType("application/vnd.ms-excel");
	}

	@Override
	protected boolean generatesDownloadContent() {
		return true;
	}

	/**
	 * Renders the Excel view, given the specified model.
	 */
	@Override
	protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

		ModelAndView modelAndView = (ModelAndView) model.get("modelAndView");
		if (null != modelAndView) {
			ModelMap modelMap = modelAndView.getModelMap();
			JawsExcelTemplate jawsExcelTemplate = null;
			try {
				jawsExcelTemplate = (JawsExcelTemplate) modelMap.get(TEMPLATE);
			} catch (Exception e) {
				log.error(Log.op("JawsExcelView.renderMergedOutputModel").msg("ModelMap template transfer error!").toString());
				throw JawsCommonExceptionCode.EXCEL_MODELMAP_ERROR.exp();
			}

			// Create a fresh workbook instance for this render step.
			XSSFWorkbook workbook = createWorkbook(jawsExcelTemplate);

			// Delegate to application-provided document code.
			buildExcelDocument(jawsExcelTemplate, workbook);

			// Set the content type.
			// response.setContentType(getContentType());
			response.setContentType("application/vnd.ms-excel");

			// Flush byte array to servlet output stream.
			renderWorkbook(workbook, jawsExcelTemplate, request, response);
		}
	}

	/**
	 * Template method for creating the POI {@link Workbook} instance.
	 * <p>
	 * The default implementation creates a traditional {@link HSSFWorkbook}. Spring-provided subclasses are overriding this for the OOXML-based
	 * variants; custom subclasses may override this for reading a workbook from a file.
	 * 
	 * @param model the model Map
	 * @param request current HTTP request (for taking the URL or headers into account)
	 * @return the new {@link Workbook} instance
	 */
	protected XSSFWorkbook createWorkbook(JawsExcelTemplate jawsExcelTemplate) {
		try {
			InputStream in = null;
			if (null != active && active.equals("prod")) {
				in = ExcelTemplateCache.getTemplateStream(jawsExcelTemplate.getTemplatePath());
			} else {
				in = Thread.currentThread().getContextClassLoader().getResourceAsStream(jawsExcelTemplate.getTemplatePath());
				if (in == null) {
					in = JawsExcelView.class.getResourceAsStream(jawsExcelTemplate.getTemplatePath());
				}
				in = new ByteArrayInputStream(IOUtils.toByteArray(in));
			}
			return new XSSFWorkbook(in);
		} catch (Exception e) {
			log.error(Log.op("JawsExcelView.createWorkbook").msg("找不到模板[{0}]", jawsExcelTemplate.getTemplatePath()).toString());
			throw JawsCommonExceptionCode.TEMPLATE_FILE_NOT_FOUND.exp(jawsExcelTemplate.getTemplatePath());
		}
	}

	/**
	 * The actual render step: taking the POI {@link Workbook} and rendering
	 * it to the given response.
	 * 
	 * @param workbook the POI Workbook to render
	 * @param response current HTTP response
	 * @throws IOException when thrown by I/O methods that we're delegating to
	 */
	protected void renderWorkbook(Workbook workbook, JawsExcelTemplate jawsExcelTemplate, HttpServletRequest request, HttpServletResponse response) throws IOException {
		String filename = jawsExcelTemplate.getFilename() + DateTime.now().toString(DATE_FORMAT) + ".xlsx";
		if (isIE(request)) {
			filename = java.net.URLEncoder.encode(filename, "UTF8");
		} else {
			filename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
		}
		response.setCharacterEncoding("UTF-8");
		response.setHeader("content-disposition", "attachment;filename=" + filename);

		ServletOutputStream out = response.getOutputStream();
		workbook.write(out);
		out.flush();
		out.close();
		// Closeable only implemented as of POI 3.10
		if (workbook instanceof Closeable) {
			((Closeable) workbook).close();
		}
	}

	/**
	 * Application-provided subclasses must implement this method to populate
	 * the Excel workbook document, given the model.
	 * 
	 * @param model the model Map
	 * @param workbook the Excel workbook to populate
	 * @param request in case we need locale etc. Shouldn't look at attributes.
	 * @param response in case we need to set cookies. Shouldn't write to it.
	 */
	protected void buildExcelDocument(JawsExcelTemplate jawsExcelTemplate, XSSFWorkbook workbook) throws Exception {
		JawsPoiExporter.export(workbook, jawsExcelTemplate.getData());
	}

}
